#pragma once

#include <efi/efi.hpp>

#define EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID                                   \
  {                                                                            \
    0x387477c2, 0x69c7, 0x11d2, {                                              \
      0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b                            \
    }                                                                          \
  }

#define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID                                    \
  {                                                                            \
    0x387477c1, 0x69c7, 0x11d2, {                                              \
      0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b                            \
    }                                                                          \
  }

#define EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID                                 \
  {                                                                            \
    0xdd9e7534, 0x7762, 0x4698, {                                              \
      0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa                           \
    }                                                                          \
  }

#define EFI_SIMPLE_POINTER_PROTOCOL_GUID                                       \
  {                                                                            \
    0x31878c87, 0xb75, 0x11d5, {                                               \
      0x9a, 0x4f, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d                           \
    }                                                                          \
  }

#define EFI_ABSOLUTE_POINTER_PROTOCOL_GUID                                     \
  {                                                                            \
    0x8d59d32b, 0xc655, 0x4ae9, {                                              \
      0x9b, 0x15, 0xf2, 0x59, 0x04, 0x99, 0x2a, 0x43                           \
    }                                                                          \
  }

#define EFI_SERIAL_IO_PROTOCOL_GUID                                            \
  {                                                                            \
    0xBB25CF6F, 0xF1D4, 0x11D2, {                                              \
      0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0xfd                           \
    }                                                                          \
  }

#define EFI_SERIAL_TERMINAL_DEVICE_TYPE_GUID                                   \
  {                                                                            \
    0x6ad9a60f, 0x5815, 0x4c7c, {                                              \
      0x8a, 0x10, 0x50, 0x53, 0xd2, 0xbf, 0x7a, 0x1b                           \
    }                                                                          \
  }

#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID                                      \
  {                                                                            \
    0x9042a9de, 0x23dc, 0x4a38, {                                              \
      0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a                           \
    }                                                                          \
  }

#define EFI_EDID_DISCOVERED_PROTOCOL_GUID                                      \
  {                                                                            \
    0x1c0c34f6, 0xd380, 0x41fa, {                                              \
      0xa0, 0x49, 0x8a, 0xd0, 0x6c, 0x1a, 0x66, 0xaa                           \
    }                                                                          \
  }

#define EFI_EDID_ACTIVE_PROTOCOL_GUID                                          \
  {                                                                            \
    0xbd8c1056, 0x9f36, 0x44ec, {                                              \
      0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86                           \
    }                                                                          \
  }

#define EFI_EDID_OVERRIDE_PROTOCOL_GUID                                        \
  {                                                                            \
    0x48ecb431, 0xfb72, 0x45c0, {                                              \
      0xa9, 0x22, 0xf4, 0x58, 0xfe, 0x04, 0x0b, 0xd5                           \
    }                                                                          \
  }

#define EFI_BLACK 0x00
#define EFI_BLUE 0x01
#define EFI_GREEN 0x02
#define EFI_CYAN (EFI_BLUE | EFI_GREEN)
#define EFI_RED 0x04
#define EFI_MAGENTA (EFI_BLUE | EFI_RED)
#define EFI_BROWN (EFI_GREEN | EFI_RED)
#define EFI_LIGHTGRAY (EFI_BLUE | EFI_GREEN | EFI_RED)
#define EFI_BRIGHT 0x08
#define EFI_DARKGRAY (EFI_BRIGHT)
#define EFI_LIGHTBLUE (EFI_BLUE | EFI_BRIGHT)
#define EFI_LIGHTGREEN (EFI_GREEN | EFI_BRIGHT)
#define EFI_LIGHTCYAN (EFI_CYAN | EFI_BRIGHT)
#define EFI_LIGHTRED (EFI_RED | EFI_BRIGHT)
#define EFI_LIGHTMAGENTA (EFI_MAGENTA | EFI_BRIGHT)
#define EFI_YELLOW (EFI_BROWN | EFI_BRIGHT)
#define EFI_WHITE (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT)

#define EFI_TEXT_ATTR(f, b) ((f) | ((b) << 4))

#define EFI_BACKGROUND_BLACK 0x00
#define EFI_BACKGROUND_BLUE 0x10
#define EFI_BACKGROUND_GREEN 0x20
#define EFI_BACKGROUND_CYAN (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN)
#define EFI_BACKGROUND_RED 0x40
#define EFI_BACKGROUND_MAGENTA (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_RED)
#define EFI_BACKGROUND_BROWN (EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED)
#define EFI_BACKGROUND_LIGHTGRAY                                               \
  (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED)

#define BOXDRAW_HORIZONTAL 0x2500
#define BOXDRAW_VERTICAL 0x2502
#define BOXDRAW_DOWN_RIGHT 0x250c
#define BOXDRAW_DOWN_LEFT 0x2510
#define BOXDRAW_UP_RIGHT 0x2514
#define BOXDRAW_UP_LEFT 0x2518
#define BOXDRAW_VERTICAL_RIGHT 0x251c
#define BOXDRAW_VERTICAL_LEFT 0x2524
#define BOXDRAW_DOWN_HORIZONTAL 0x252c
#define BOXDRAW_UP_HORIZONTAL 0x2534
#define BOXDRAW_VERTICAL_HORIZONTAL 0x253c

#define BOXDRAW_DOUBLE_HORIZONTAL 0x2550
#define BOXDRAW_DOUBLE_VERTICAL 0x2551
#define BOXDRAW_DOWN_RIGHT_DOUBLE 0x2552
#define BOXDRAW_DOWN_DOUBLE_RIGHT 0x2553
#define BOXDRAW_DOUBLE_DOWN_RIGHT 0x2554

#define BOXDRAW_DOWN_LEFT_DOUBLE 0x2555
#define BOXDRAW_DOWN_DOUBLE_LEFT 0x2556
#define BOXDRAW_DOUBLE_DOWN_LEFT 0x2557

#define BOXDRAW_UP_RIGHT_DOUBLE 0x2558
#define BOXDRAW_UP_DOUBLE_RIGHT 0x2559
#define BOXDRAW_DOUBLE_UP_RIGHT 0x255a

#define BOXDRAW_UP_LEFT_DOUBLE 0x255b
#define BOXDRAW_UP_DOUBLE_LEFT 0x255c
#define BOXDRAW_DOUBLE_UP_LEFT 0x255d

#define BOXDRAW_VERTICAL_RIGHT_DOUBLE 0x255e
#define BOXDRAW_VERTICAL_DOUBLE_RIGHT 0x255f
#define BOXDRAW_DOUBLE_VERTICAL_RIGHT 0x2560

#define BOXDRAW_VERTICAL_LEFT_DOUBLE 0x2561
#define BOXDRAW_VERTICAL_DOUBLE_LEFT 0x2562
#define BOXDRAW_DOUBLE_VERTICAL_LEFT 0x2563

#define BOXDRAW_DOWN_HORIZONTAL_DOUBLE 0x2564
#define BOXDRAW_DOWN_DOUBLE_HORIZONTAL 0x2565
#define BOXDRAW_DOUBLE_DOWN_HORIZONTAL 0x2566

#define BOXDRAW_UP_HORIZONTAL_DOUBLE 0x2567
#define BOXDRAW_UP_DOUBLE_HORIZONTAL 0x2568
#define BOXDRAW_DOUBLE_UP_HORIZONTAL 0x2569

#define BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE 0x256a
#define BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL 0x256b
#define BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL 0x256c

#define BLOCKELEMENT_FULL_BLOCK 0x2588
#define BLOCKELEMENT_LIGHT_SHADE 0x2591

#define GEOMETRICSHAPE_UP_TRIANGLE 0x25b2
#define GEOMETRICSHAPE_RIGHT_TRIANGLE 0x25ba
#define GEOMETRICSHAPE_DOWN_TRIANGLE 0x25bc
#define GEOMETRICSHAPE_LEFT_TRIANGLE 0x25c4

#define ARROW_UP 0x2191
#define ARROW_DOWN 0x2193

#define CHAR_NULL 0x00
#define CHAR_BACKSPACE 0x08
#define CHAR_TAB 0x09
#define CHAR_LINEFEED 0x0A
#define CHAR_CARRIAGE_RETURN 0x0D

#define SCAN_NULL 0x00
#define SCAN_UP 0x01
#define SCAN_DOWN 0x02
#define SCAN_RIGHT 0x03
#define SCAN_LEFT 0x04
#define SCAN_HOME 0x05
#define SCAN_END 0x06
#define SCAN_INSERT 0x07
#define SCAN_DELETE 0x08
#define SCAN_PAGE_UP 0x09
#define SCAN_PAGE_DOWN 0x0A
#define SCAN_F1 0x0B
#define SCAN_F2 0x0C
#define SCAN_F3 0x0D
#define SCAN_F4 0x0E
#define SCAN_F5 0x0F
#define SCAN_F6 0x10
#define SCAN_F7 0x11
#define SCAN_F8 0x12
#define SCAN_F9 0x13
#define SCAN_F10 0x14
#define SCAN_F11 0x15
#define SCAN_F12 0x16
#define SCAN_ESC 0x17

// 用于SimpleTextInputExProtocol::KeyState::keyShiftState

#define EFI_SHIFT_STATE_VALID BIT(31)
#define EFI_RIGHT_SHIFT_PRESSED BIT(0)
#define EFI_LEFT_SHIFT_PRESSED BIT(1)
#define EFI_RIGHT_CONTROL_PRESSED BIT(2)
#define EFI_LEFT_CONTROL_PRESSED BIT(3)
#define EFI_RIGHT_ALT_PRESSED BIT(4)
#define EFI_LEFT_ALT_PRESSED BIT(5)
#define EFI_RIGHT_LOGO_PRESSED BIT(6)
#define EFI_LEFT_LOGO_PRESSED BIT(7)
#define EFI_MENU_KEY_PRESSED BIT(8)
#define EFI_SYS_REQ_PRESSED BIT(9)

// 用于SimpleTextInputExProtocol::KeyState::keyToggleState

#define EFI_TOGGLE_STATE_VALID BIT(7)
#define EFI_KEY_STATE_EXPOSED BIT(6)
#define EFI_SCROLL_LOCK_ACTIVE BIT(0)
#define EFI_NUM_LOCK_ACTIVE BIT(1)
#define EFI_CAPS_LOCK_ACTIVE BIT(2)

// 用于AbsolutePointerProtocol::PointerState::activeButtons

#define EFI_ABSP_TouchActive BIT(0)
#define EFI_ABS_AltActive BIT(1)

// 用于AbsolutePointerProtocol::PointerMode::attribute

#define EFI_ABSP_SupportsAltActive BIT(0)
#define EFI_ABSP_SupportsPressureAsZ BIT(1)

// SerialIoProtocol的controlBit

#define EFI_SERIAL_DATA_TERMINAL_READY BIT(0)
#define EFI_SERIAL_REQUEST_TO_SEND BIT(1)
#define EFI_SERIAL_CLEAR_TO_SEND BIT(4)
#define EFI_SERIAL_DATA_SET_READY BIT(5)
#define EFI_SERIAL_RING_INDICATE BIT(6)
#define EFI_SERIAL_CARRIER_DETECT BIT(7)
#define EFI_SERIAL_INPUT_BUFFER_EMPTY BIT(8)
#define EFI_SERIAL_OUTPUT_BUFFER_EMPTY BIT(9)
#define EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE BIT(12)
#define EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE BIT(13)
#define EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE BIT(14)

// 用于EdidOverrideProtocol::GetEdid(attributes)

#define EFI_EDID_OVERRIDE_DONT_OVERRIDE BIT(0)
#define EFI_EDID_OVERRIDE_ENABLE_HOT_PLUG BIT(1)

namespace efi {

template <typename T>
using __ConsoleReset
    = Status(EFIAPI *)(IN T *instance, IN bool extendedVerification);

typedef struct SimpleTextOutputProtocol {
  typedef struct {
    int32_t maxMode;

    int32_t mode;
    int32_t attribute;
    int32_t cursorColumn;
    int32_t cursorRow;
    bool cursorVisible;
  } OptputMode;

  typedef Status(EFIAPI *OutputString)(
      IN struct SimpleTextOutputProtocol *instance,
      IN const char16_t *string
  );
  typedef Status(EFIAPI *TestString)(
      IN struct SimpleTextOutputProtocol *instance,
      IN const char16_t *string
  );
  typedef Status(EFIAPI *QueryMode)(
      IN struct SimpleTextOutputProtocol *instance,
      IN uint64_t modeNumber,
      OUT uint64_t *columns,
      OUT uint64_t *rows
  );
  typedef Status(EFIAPI *SetMode)(
      IN struct SimpleTextOutputProtocol *instance,
      IN uint64_t modeNumber
  );
  typedef Status(EFIAPI *SetAttribute)(
      IN struct SimpleTextOutputProtocol *instance,
      IN uint64_t attribute
  );
  typedef Status(EFIAPI *ClearScreen)(
      IN struct SimpleTextOutputProtocol *instance
  );
  typedef Status(EFIAPI *SetCursorPosition)(
      IN struct SimpleTextOutputProtocol *instance,
      IN uint64_t column,
      IN uint64_t row
  );
  typedef Status(EFIAPI *EnableCursor)(
      IN struct SimpleTextOutputProtocol *instance,
      IN bool enable
  );

  __ConsoleReset<struct SimpleTextOutputProtocol> reset;

  OutputString outputString;
  TestString testString;

  QueryMode queryMode;
  SetMode setMode;
  SetAttribute setAttribute;

  ClearScreen clearScreen;
  SetCursorPosition setCursorPosition;
  EnableCursor enableCursor;

  OptputMode *currentMode;
} SimpleTextOutputProtocol;

typedef struct SimpleTextInputProtocol {
  typedef struct {
    uint16_t scanCode;
    char16_t unicodeChar;
  } InputKey;

  typedef Status(EFIAPI *ReadKey)(
      IN struct SimpleTextInputProtocol *instance,
      OUT InputKey *key
  );

  __ConsoleReset<struct SimpleTextInputProtocol> reset;
  ReadKey readKeyStroke;
  Event::Instance waitForKey;
} SimpleTextInputProtocol;

typedef struct SimpleTextInputExProtocol {
  typedef struct {
    uint32_t keyShiftState;
    uint8_t keyToggleState;
  } KeyState;

  typedef struct {
    SimpleTextInputProtocol::InputKey key;
    KeyState state;
  } KeyData;

  typedef Status(EFIAPI *KeyNotifyCallback)(IN KeyData *keyData);

  typedef Status(EFIAPI *ReadKeyEx)(
      IN struct SimpleTextInputExProtocol *instance,
      OUT KeyData *keyData
  );

  typedef Status(EFIAPI *SetState)(
      IN struct SimpleTextInputExProtocol *instance,
      IN uint8_t *keyToggleState
  );

  typedef Status(EFIAPI *RegisterKeyNotify)(
      IN struct SimpleTextInputExProtocol *instance,
      IN KeyData *keyData,
      IN KeyNotifyCallback callback,
      OUT Handle *notifyHandle
  );

  typedef Status(EFIAPI *UnregisterKeyNotify)(
      IN struct SimpleTextInputExProtocol *instance,
      IN Handle notificationHandle
  );

  __ConsoleReset<struct SimpleTextInputExProtocol> reset;
  ReadKeyEx readKeyStrokeEx;
  Event::Instance waitForKeyEx;
  SetState setState;
  RegisterKeyNotify registerKeyNotify;
  UnregisterKeyNotify unregisterKeyNotify;
} SimpleTextInputExProtocol;

typedef struct SimplePointerProtocol {
  typedef struct {
    int32_t relativeMovementX;
    int32_t relativeMovementY;
    int32_t relativeMovementZ;
    bool leftButton;
    bool rightButton;
  } PointerState;

  typedef struct {
    uint64_t resolutionX;
    uint64_t resolutionY;
    uint64_t resolutionZ;
    bool leftButton;
    bool rightButton;
  } PointerMode;

  typedef Status(EFIAPI *GetState)(
      IN struct SimplePointerProtocol *instance,
      OUT PointerState *state
  );

  __ConsoleReset<struct SimplePointerProtocol> reset;
  GetState getState;
  Event::Instance waitForInput;
  PointerMode *mode;
} SimplePointerProtocol;

typedef struct AbsolutePointerProtocol {
  typedef struct {
    uint64_t currentX;
    uint64_t currentY;
    uint64_t currentZ;
    uint32_t activeButtons;
  } PointerState;

  typedef struct {
    uint64_t absoluteMinX;
    uint64_t absoluteMinY;
    uint64_t absoluteMinZ;
    uint64_t absoluteMaxX;
    uint64_t absoluteMaxY;
    uint64_t absoluteMaxZ;
    uint32_t attributes;
  } PointerMode;

  typedef Status(EFIAPI *GetState)(
      IN struct AbsolutePointerProtocol *instance,
      OUT PointerState *state
  );

  __ConsoleReset<struct AbsolutePointerProtocol> reset;
  GetState getState;
  Event::Instance waitForInput;
  PointerMode *mode;
} AbsolutePointerProtocol;

typedef struct SerialIoProtocol {
  enum class ParityType : uint32_t {
    Default,
    NoParity,
    EvenParity,
    OddParity,
    MarkParity,
    SpaceParity
  };

  enum class StopBitsType : uint32_t {
    Default,
    OneStopBit,      // 1 stop bit
    OneFiveStopBits, // 1.5 stop bits
    TwoStopBits      // 2 stop bits
  };

  typedef struct {
    uint32_t controlMask;

    uint32_t timeout;
    uint64_t baudRate;
    uint32_t receiveFifoDepth;
    uint32_t dataBits;
    uint32_t parity;
    uint32_t stopBits;
  } SerialIoMode;

  typedef Status(EFIAPI *Reset)(IN struct SerialIoProtocol *instance);

  typedef Status(EFIAPI *SetAttributes)(
      IN struct SerialIoProtocol *instance,
      IN uint64_t baudRate,
      IN uint32_t receiveFifoDepth,
      IN uint32_t timeout,
      IN ParityType parity,
      IN uint8_t dataBits,
      IN StopBitsType stopBits
  );

  typedef Status(EFIAPI *SetControlBits)(
      IN struct SerialIoProtocol *instance,
      IN uint32_t controlBits
  );

  typedef Status(EFIAPI *GetControlBits)(
      IN struct SerialIoProtocol *instance,
      OUT uint32_t *controlBits
  );

  typedef Status(EFIAPI *Write)(
      IN struct SerialIoProtocol *instance,
      IN OUT uint64_t *size,
      IN void *data
  );

  typedef Status(EFIAPI *Read)(
      IN struct SerialIoProtocol *instance,
      IN OUT uint64_t *size,
      OUT void *data
  );

  uint32_t revision;
  Reset reset;
  SetAttributes setAttributes;
  SetControlBits setControlBits;
  GetControlBits getControlBits;
  Write write;
  Read read;
  SerialIoMode *mode;

  const Guid *deviceTypeGuid; // 当revision为1.1(0x00010001)时有效
} SerialIoProtocol;

typedef struct GraphicsOutputProtocol {
  typedef struct {
    uint32_t redMask;
    uint32_t greenMask;
    uint32_t blueMask;
    uint32_t reservedMask;
  } PixelBitmask;

  typedef struct {
    uint8_t blue;
    uint8_t green;
    uint8_t red;
    uint8_t reserved;
  } BltPixel;

  enum class PixelFormat : uint32_t {
    RBGRColor, //  0x00bbggrr
    RRGBColor, //  0x00rrggbb
    PixelBitMask,
    PixelBltOnly
  };

  enum class BltOperation : uint32_t {
    FillVideo,
    VideoToBuffer,
    BufferToVideo,
    VideoToVideo
  };

  typedef struct {
    uint32_t version;
    uint32_t horizontalResolution;
    uint32_t verticalResolution;
    PixelFormat pixelFormat;
    PixelBitmask pixelInformation;
    uint32_t pixelsPerScanLine;
  } ModeInfo;

  typedef struct {
    uint32_t maxMode;
    uint32_t mode;
    ModeInfo *info;
    uint64_t sizeOfInfo;
    uintptr_t frameBufferBase;
    uint64_t frameBufferSize;
  } Mode;

  typedef Status(EFIAPI *QueryMode)(
      IN struct GraphicsOutputProtocol *instance,
      IN uint32_t modeNumber,
      OUT uint64_t *sizeOfInfo,
      OUT ModeInfo **info
  );

  typedef Status(EFIAPI *SetMode)(
      IN struct GraphicsOutputProtocol *instance,
      IN uint32_t modeNumber
  );

  typedef Status(EFIAPI *Blt)(
      IN struct GraphicsOutputProtocol *instance,
      IN OUT BltPixel *bltBuffer,
      IN BltOperation operation OPTIONAL,
      IN uint64_t sourceX,
      IN uint64_t sourceY,
      IN uint64_t destinationX,
      IN uint64_t destinationY,
      IN uint64_t width,
      IN uint64_t height,
      IN uint64_t delta OPTIONAL
  );

  QueryMode queryMode;
  SetMode setMode;
  Blt blt;
  Mode *mode;
} GraphicsOutputProtocol;

typedef struct {
  uint32_t sizeOfEdid;
  uint8_t *edid;
} EdidInformationProtocol;

typedef struct EdidOverrideProtocol {
  typedef Status(EFIAPI *GetEdid)(
      IN struct EdidOverrideProtocol *instance,
      IN Handle *childHandle,
      OUT uint32_t *attributes,
      OUT uint64_t *edidSize,
      OUT uint8_t **edid
  );

  GetEdid getEdid;
} EdidOverrideProtocol;

} // namespace efi